<template>
    <view>
        <canvas
            canvas-id="canvasdrawer"
            :style="{
                width: _this.width === '100%' ? _this.width : _this.width + 'px',
                height: _this.height + 'px'
            }"
            class="board"
            v-if="_this.showCanvas"></canvas>
    </view>
</template>

<script setup>
import { watch, onMounted, getCurrentInstance } from 'vue';

const Instance = getCurrentInstance();

const props = defineProps({
    painting: {
        type: Object
    }
});

const emit = defineEmits(['getImage']);

const _this = {
    showCanvas: false,
    width: 100,
    height: 100,
    tempFileList: [],
    isPainting: false,
    ctx: null,
    cache: Array()
};

const readyPigment = () => {
    // const { width, height, views } = props.painting
    const width = props.painting.width;
    const height = props.painting.height;
    const views = props.painting.views;

    _this.width = width;
    _this.height = height;

    const inter = setInterval(() => {
        if (_this.ctx) {
            clearInterval(inter);
            _this.ctx.clearActions();
            _this.ctx.save();
            getImagesInfo(views);
        }
    }, 100);
};

watch(
    () => props.painting,
    (newVal, oldVal) => {
        if (!_this.isPainting) {
            if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
                if (newVal && newVal.width && newVal.height) {
                    _this.showCanvas = true;
                    _this.isPainting = true;

                    readyPigment();
                }
            } else {
                if (newVal && newVal.mode !== 'same') {
                    // _this.triggerEvent('getImage', {errMsg: 'canvasdrawer:samme params'})
                    emit('getImage', {
                        errMsg: 'canvasdrawer:same params'
                    });
                }
            }
        }
    },
    {
        immediate: true
    }
);

const getImagesInfo = views => {
    const imageList = [];
    for (let i = 0; i < views.length; i++) {
        if (views[i].type === 'image') {
            imageList.push(getImageInfo(views[i].url));
        }
    }

    const loadTask = [];
    for (let i = 0; i < Math.ceil(imageList.length / 8); i++) {
        loadTask.push(
            new Promise((resolve, reject) => {
                Promise.all(imageList.splice(i * 8, 8))
                    .then(res => {
                        resolve(res);
                    })
                    .catch(res => {
                        reject(res);
                    });
            })
        );
    }
    Promise.all(loadTask).then(res => {
        let tempFileList = [];
        for (let i = 0; i < res.length; i++) {
            tempFileList = tempFileList.concat(res[i]);
        }
        _this.tempFileList = tempFileList;
        startPainting();
    });
};

const startPainting = () => {
    // 填充canvas背景色
    _this.ctx.setFillStyle(props.painting?.background || '#fff');
    _this.ctx.fillRect(0, 0, _this.width, _this.height);

    // 填充其他元素
    const tempFileList = _this.tempFileList;
    const views = props.painting.views;

    // const { tempFileList, painting: { views } } = _this.$data
    for (let i = 0, imageIndex = 0; i < views.length; i++) {
        if (views[i].type === 'image') {
            drawImage({
                ...views[i],
                url: tempFileList[imageIndex]
            });
            imageIndex++;
        } else if (views[i].type === 'text') {
            if (!_this.ctx.measureText) {
                uni.showModal({
                    title: '提示',
                    content: '当前微信版本过低，无法使用 measureText 功能，请升级到最新微信版本后重试。'
                });
                // _this.triggerEvent('getImage', {errMsg: 'canvasdrawer:version too low'})
                emit('getImage', {
                    errMsg: 'canvasdrawer:version too low'
                });
                return;
            } else {
                drawText(views[i]);
            }
        } else if (views[i].type === 'rect') {
            drawRect(views[i]);
        } else if (views[i].type === 'roundRect') {
            drawRectRound(views[i]);
        }
    }
    _this.ctx.draw(
        false,
        (() => {
            uni.setStorageSync('canvasdrawer_pic_cache', _this.cache);
            const system = uni.getSystemInfoSync().system;
            if (/ios/i.test(system)) {
                // saveImageToLocal(); ios是否有延迟
                setTimeout(() => {
                    saveImageToLocal();
                }, 800);
            } else {
                // 延迟保存图片，解决安卓生成图片错位bug。
                setTimeout(() => {
                    saveImageToLocal();
                }, 800);
            }
        })()
    );
    // _this.ctx.draw();
};

const drawImage = params => {
    _this.ctx.save();
    const { url, top = 0, left = 0, width = 0, height = 0, borderRadius = 0, isCircle, deg = 0 } = params;
    if (isCircle) {
        // 绘制圆形头像
        _this.ctx.beginPath();
        _this.ctx.arc(width / 2 + left, height / 2 + top, width / 2, 0, Math.PI * 2, false);
        // _this.ctx.setFillStyle('#000');
        // _this.ctx.fill(); //保证图片无bug填充
        _this.ctx.closePath();
        _this.ctx.clip(); //剪切
    }
    if (deg !== 0) {
        _this.ctx.translate(left + width / 2, top + height / 2);
        _this.ctx.rotate((deg * Math.PI) / 180);
        if (typeof url != 'undefined') {
            _this.ctx.drawImage(url, -width / 2, -height / 2, width, height);
        }
    } else {
        if (typeof url != 'undefined') {
            _this.ctx.drawImage(url, left, top, width, height);
        }
    }

    _this.ctx.restore();
};

const drawText = params => {
    _this.ctx.save();
    const {
        MaxLineNumber = 2,
        breakWord = false,
        color = 'black',
        content = '',
        fontSize = 16,
        top = 0,
        left = 0,
        lineHeight = 20,
        textAlign = 'left',
        width,
        bolder = false,
        textDecoration = 'none'
    } = params;

    _this.ctx.beginPath();
    _this.ctx.setTextBaseline('top');
    _this.ctx.setTextAlign(textAlign);
    _this.ctx.setFillStyle(color);
    _this.ctx.setFontSize(fontSize);

    if (!breakWord) {
        _this.ctx.fillText(content, left, top);
        drawTextLine(left, top, textDecoration, color, fontSize, content);
    } else {
        let fillText = '';
        let fillTop = top;
        let lineNum = 1;
        for (let i = 0; i < content.length; i++) {
            fillText += [content[i]];
            if (_this.ctx.measureText(fillText).width > width) {
                if (lineNum === MaxLineNumber) {
                    if (i !== content.length) {
                        fillText = fillText.substring(0, fillText.length - 1) + '...';
                        _this.ctx.fillText(fillText, left, fillTop);
                        drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText);
                        fillText = '';
                        break;
                    }
                }
                _this.ctx.fillText(fillText, left, fillTop);
                drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText);
                fillText = '';
                fillTop += lineHeight;
                lineNum++;
            }
        }
        _this.ctx.fillText(fillText, left, fillTop);
        drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText);
    }

    _this.ctx.restore();

    if (bolder) {
        drawText({
            ...params,
            left: left + 0.3,
            top: top + 0.3,
            bolder: false,
            textDecoration: 'none'
        });
    }
};
const drawTextLine = (left, top, textDecoration, color, fontSize, content) => {
    if (textDecoration === 'underline') {
        drawRect({
            background: color,
            top: top + fontSize * 1.2,
            left: left - 1,
            width: _this.ctx.measureText(content).width + 3,
            height: 1
        });
    } else if (textDecoration === 'line-through') {
        drawRect({
            background: color,
            top: top + fontSize * 0.6,
            left: left - 1,
            width: _this.ctx.measureText(content).width + 3,
            height: 1
        });
    }
};
const drawRect = params => {
    _this.ctx.save();
    const { background, top = 0, left = 0, width = 0, height = 0 } = params;
    _this.ctx.setFillStyle(background);
    _this.ctx.fillRect(left, top, width, height);
    _this.ctx.restore();
};
const roundRect = (x, y, width, height, radius) => {
    // 圆角矩形
    _this.ctx.beginPath();
    _this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
    _this.ctx.lineTo(width - radius + x, y);
    _this.ctx.arc(width - radius + x, radius + y, radius, (Math.PI * 3) / 2, Math.PI * 2);
    _this.ctx.lineTo(width + x, height + y - radius);
    _this.ctx.arc(width - radius + x, height - radius + y, radius, 0, (Math.PI * 1) / 2);
    _this.ctx.lineTo(radius + x, height + y);
    _this.ctx.arc(radius + x, height - radius + y, radius, (Math.PI * 1) / 2, Math.PI);
    _this.ctx.closePath();
    _this.ctx.clip(); //剪切
};
const drawRectRound = params => {
    // 绘制圆角矩形
    _this.ctx.save();
    const {
        content,
        color,
        textAlign,
        fontSize,
        background,
        top = 0,
        left = 0,
        width = 0,
        height = 0,
        radius = 0
    } = params;
    roundRect(left, top, width, height, radius);
    if (background) {
        _this.ctx.setFillStyle(background);
        _this.ctx.fillRect(left, top, width, height);
    } else {
        _this.ctx.strokeStyle = background; // 线条颜色
        _this.ctx.stroke();
    }
    if (content) {
        // 如果有文案填充文案
        _this.ctx.setTextBaseline('top');
        _this.ctx.setTextAlign(textAlign);
        _this.ctx.setFillStyle(color);
        _this.ctx.setFontSize(fontSize);
        _this.ctx.fillText(content, left + (width - fontSize * content.length) / 2, top + (height - fontSize) / 2 - 1);
    }
    _this.ctx.restore();
};
const getImageInfo = url => {
    return new Promise((resolve, reject) => {
        if (_this.cache[url]) {
            resolve(_this.cache[url]);
        } else {
            // eslint-disable-next-line
            const objExp = new RegExp(/^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/);
            if (objExp.test(url)) {
                uni.getImageInfo({
                    src: url,
                    complete: res => {
                        if (res.errMsg === 'getImageInfo:ok') {
                            _this.cache[url] = res.path;
                            resolve(res.path);
                        } else {
                            // _this.triggerEvent('getImage', {errMsg: 'canvasdrawer:download fail'})
                            emit('getImage', {
                                errMsg: 'canvasdrawer:download fail ' + url
                            });
                            reject(new Error('getImageInfo fail'));
                        }
                    }
                });
            } else {
                _this.cache[url] = url;
                resolve(url);
            }
        }
    }).catch(error => {});
};
const saveImageToLocal = () => {
    // const { width, height } = _this.data
    const width = _this.width;
    const height = _this.height;
    let pixelRatioParams = {};
    if (props.painting.isPixelRatio) {
        // 获取PixelRatio， 设置destWidth， destHeight避免图片模糊
        const { devicePixelRatio } = uni.getSystemInfoSync() || { devicePixelRatio: 1 };
        pixelRatioParams = {
            destWidth: width * devicePixelRatio,
            destHeight: height * devicePixelRatio
        };
    }
    uni.canvasToTempFilePath(
        {
            x: 0,
            y: 0,
            width,
            height,
            ...pixelRatioParams,
            canvasId: 'canvasdrawer',
            complete: res => {
                if (res.errMsg === 'canvasToTempFilePath:ok') {
                    _this.showCanvas = false;
                    _this.isPainting = false;
                    _this.tempFileList = [];

                    // _this.triggerEvent('getImage', {tempFilePath: res.tempFilePath, errMsg: 'canvasdrawer:ok'})
                    emit('getImage', {
                        tempFilePath: res?.tempFilePath,
                        errMsg: 'canvasdrawer:ok'
                    });
                } else {
                    // _this.triggerEvent('getImage', {errMsg: 'canvasdrawer:fail'})
                    emit('getImage', {
                        errMsg: 'canvasdrawer:fail'
                    });
                }
            }
        },
        Instance
    );
};

onMounted(() => {
    uni.removeStorageSync('canvasdrawer_pic_cache');
    _this.ctx = uni.createCanvasContext('canvasdrawer', Instance);
});
</script>

<style>
/* .board {
    position: fixed;
    top: 2000rpx;
} */
</style>
